/**
  ******************************************************************************
    @file    usbd_audio_core.c
    @author  MCD Application Team
    @version V1.1.0
    @date    19-March-2012
    @brief   This file provides the high layer firmware functions to manage the
             following functionalities of the USB Audio Class:
              - Initialization and Configuration of high and low layer
              - Enumeration as Audio Streaming Device
              - Audio Streaming data transfer
              - AudioControl requests management
              - Error management

     @verbatim

             ===================================================================
                                   Audio Class Driver Description
             ===================================================================
              This driver manages the Audio Class 1.0 following the "USB Device Class Definition for
              Audio Devices V1.0 Mar 18, 98".
              This driver implements the following aspects of the specification:
                - Device descriptor management
                - Configuration descriptor management
                - Standard AC Interface Descriptor management
                - 1 Audio Streaming Interface (with single channel, PCM, Stereo mode)
                - 1 Audio Streaming Endpoint
                - 1 Audio Terminal Input (1 channel)
                - Audio Class-Specific AC Interfaces
                - Audio Class-Specific AS Interfaces
                - AudioControl Requests: only SET_CUR and GET_CUR requests are supported (for Mute)
                - Audio Feature Unit (limited to Mute control)
                - Audio Synchronization type: Asynchronous
                - Single fixed audio sampling rate (configurable in usbd_conf.h file)

              @note
               The Audio Class 1.0 is based on USB Specification 1.0 and thus supports only
               Low and Full speed modes and does not allow High Speed transfers.
               Please refer to "USB Device Class Definition for Audio Devices V1.0 Mar 18, 98"
               for more details.

              These aspects may be enriched or modified for a specific user application.

               This driver doesn't implement the following aspects of the specification
               (but it is possible to manage these features with some modifications on this driver):
                - AudioControl Endpoint management
                - AudioControl requsests other than SET_CUR and GET_CUR
                - Abstraction layer for AudioControl requests (only Mute functionality is managed)
                - Audio Synchronization type: Adaptive
                - Audio Compression modules and interfaces
                - MIDI interfaces and modules
                - Mixer/Selector/Processing/Extension Units (Feature unit is limited to Mute control)
                - Any other application-specific modules
                - Multiple and Variable audio sampling rates
                - Out Streaming Endpoint/Interface (microphone)

     @endverbatim

  ******************************************************************************
    @attention

    <h2><center>&copy; COPYRIGHT 2012 STMicroelectronics</center></h2>

    Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
    You may not use this file except in compliance with the License.
    You may obtain a copy of the License at:

           http://www.st.com/software_license_agreement_liberty_v2

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

  ******************************************************************************
*/

/* Includes ------------------------------------------------------------------*/

#include "usbd_audio_core.h"
#include "usbd_audio_out_if.h"

/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
    @{
*/


/** @defgroup usbd_audio
    @brief usbd core module
    @{
*/

/** @defgroup usbd_audio_Private_TypesDefinitions
    @{
*/
/**
    @}
*/


/** @defgroup usbd_audio_Private_Defines
    @{
*/
/**
    @}
*/


/** @defgroup usbd_audio_Private_Macros
    @{
*/
/**
    @}
*/


/** @defgroup usbd_audio_Private_FunctionPrototypes
    @{
*/

/*********************************************
    AUDIO Device library callbacks
 *********************************************/
static uint8_t  usbd_audio_Init       (void  *pdev, uint8_t cfgidx);
static uint8_t  usbd_audio_DeInit     (void  *pdev, uint8_t cfgidx);
static uint8_t  usbd_audio_Setup      (void  *pdev, USB_SETUP_REQ *req);
static uint8_t  usbd_audio_EP0_RxReady(void *pdev);
static uint8_t  usbd_audio_DataIn     (void *pdev, uint8_t epnum);
static uint8_t  usbd_audio_DataOut    (void *pdev, uint8_t epnum);
static uint8_t  usbd_audio_SOF        (void *pdev);
static uint8_t  usbd_audio_OUT_Incplt (void  *pdev);

/*********************************************
    AUDIO Requests management functions
 *********************************************/
static void AUDIO_Req_GetCurrent(void *pdev, USB_SETUP_REQ *req);
static void AUDIO_Req_SetCurrent(void *pdev, USB_SETUP_REQ *req);
static uint8_t  *USBD_audio_GetCfgDesc (uint8_t speed, uint16_t *length);
/**
    @}
*/

/** @defgroup usbd_audio_Private_Variables
    @{
*/
/* Main Buffer for Audio Data Out transfers and its relative pointers */
uint8_t  IsocOutBuff [TOTAL_OUT_BUF_SIZE * 2];
uint8_t* IsocOutWrPtr = IsocOutBuff;
uint8_t* IsocOutRdPtr = IsocOutBuff;

/* Main Buffer for Audio Control Rrequests transfers and its relative variables */
uint8_t  AudioCtl[64];
uint8_t  AudioCtlCmd = 0;
uint32_t AudioCtlLen = 0;
uint8_t  AudioCtlUnit = 0;

static uint32_t PlayFlag = 0;

static __IO uint32_t  usbd_audio_AltSet = 0;
static uint8_t usbd_audio_CfgDesc[AUDIO_CONFIG_DESC_SIZE];

/* AUDIO interface class callbacks structure */
USBD_Class_cb_TypeDef  AUDIO_cb =
{
	usbd_audio_Init,
	usbd_audio_DeInit,
	usbd_audio_Setup,
	NULL, /* EP0_TxSent */
	usbd_audio_EP0_RxReady,
	usbd_audio_DataIn,
	usbd_audio_DataOut,
	usbd_audio_SOF,
	NULL,
	usbd_audio_OUT_Incplt,
	USBD_audio_GetCfgDesc,
#ifdef USB_OTG_HS_CORE
	USBD_audio_GetCfgDesc, /* use same config as per FS */
#endif
};

/* USB AUDIO device Configuration Descriptor */
static uint8_t usbd_audio_CfgDesc[AUDIO_CONFIG_DESC_SIZE] =
{
	/* Configuration 1 */
	0x09,                                 /* bLength */
	USB_CONFIGURATION_DESCRIPTOR_TYPE,    /* bDescriptorType */
	LOBYTE(AUDIO_CONFIG_DESC_SIZE),       /* wTotalLength  109 bytes*/
	HIBYTE(AUDIO_CONFIG_DESC_SIZE),
	0x02,                                 /* bNumInterfaces */
	0x01,                                 /* bConfigurationValue */
	0x00,                                 /* iConfiguration */
	0xC0,                                 /* bmAttributes  BUS Powred*/
	0x32,                                 /* bMaxPower = 100 mA*/
	/* 09 byte*/

	/* USB Speaker Standard interface descriptor */
	AUDIO_INTERFACE_DESC_SIZE,            /* bLength */
	USB_INTERFACE_DESCRIPTOR_TYPE,        /* bDescriptorType */
	0x00,                                 /* bInterfaceNumber */
	0x00,                                 /* bAlternateSetting */
	0x00,                                 /* bNumEndpoints */
	USB_DEVICE_CLASS_AUDIO,               /* bInterfaceClass */
	AUDIO_SUBCLASS_AUDIOCONTROL,          /* bInterfaceSubClass */
	AUDIO_PROTOCOL_UNDEFINED,             /* bInterfaceProtocol */
	0x00,                                 /* iInterface */
	/* 09 byte*/

	/* USB Speaker Class-specific AC Interface Descriptor */
	AUDIO_INTERFACE_DESC_SIZE,            /* bLength */
	AUDIO_INTERFACE_DESCRIPTOR_TYPE,      /* bDescriptorType */
	AUDIO_CONTROL_HEADER,                 /* bDescriptorSubtype */
	0x00,          /* 1.00 */             /* bcdADC */
	0x01,
	0x27,                                 /* wTotalLength = 39*/
	0x00,
	0x01,                                 /* bInCollection */
	0x01,                                 /* baInterfaceNr */
	/* 09 byte*/

	/* USB Speaker Input Terminal Descriptor */
	AUDIO_INPUT_TERMINAL_DESC_SIZE,       /* bLength */
	AUDIO_INTERFACE_DESCRIPTOR_TYPE,      /* bDescriptorType */
	AUDIO_CONTROL_INPUT_TERMINAL,         /* bDescriptorSubtype */
	0x01,                                 /* bTerminalID */
	0x01,                                 /* wTerminalType AUDIO_TERMINAL_USB_STREAMING   0x0101 */
	0x01,
	0x00,                                 /* bAssocTerminal */
	0x01,                                 /* bNrChannels */
	0x00,                                 /* wChannelConfig 0x0000  Mono */
	0x00,
	0x00,                                 /* iChannelNames */
	0x00,                                 /* iTerminal */
	/* 12 byte*/

	/* USB Speaker Audio Feature Unit Descriptor */
	0x09,                                 /* bLength */
	AUDIO_INTERFACE_DESCRIPTOR_TYPE,      /* bDescriptorType */
	AUDIO_CONTROL_FEATURE_UNIT,           /* bDescriptorSubtype */
	AUDIO_OUT_STREAMING_CTRL,             /* bUnitID */
	0x01,                                 /* bSourceID */
	0x01,                                 /* bControlSize */
	AUDIO_CONTROL_MUTE,                   /* bmaControls(0) */
	0x00,                                 /* bmaControls(1) */
	0x00,                                 /* iTerminal */
	/* 09 byte*/

	/*USB Speaker Output Terminal Descriptor */
	0x09,      /* bLength */
	AUDIO_INTERFACE_DESCRIPTOR_TYPE,      /* bDescriptorType */
	AUDIO_CONTROL_OUTPUT_TERMINAL,        /* bDescriptorSubtype */
	0x03,                                 /* bTerminalID */
	0x01,                                 /* wTerminalType  0x0301*/
	0x03,
	0x00,                                 /* bAssocTerminal */
	0x02,                                 /* bSourceID */
	0x00,                                 /* iTerminal */
	/* 09 byte*/

	/* USB Speaker Standard AS Interface Descriptor - Audio Streaming Zero Bandwith */
	/* Interface 1, Alternate Setting 0                                             */
	AUDIO_INTERFACE_DESC_SIZE,  /* bLength */
	USB_INTERFACE_DESCRIPTOR_TYPE,        /* bDescriptorType */
	0x01,                                 /* bInterfaceNumber */
	0x00,                                 /* bAlternateSetting */
	0x00,                                 /* bNumEndpoints */
	USB_DEVICE_CLASS_AUDIO,               /* bInterfaceClass */
	AUDIO_SUBCLASS_AUDIOSTREAMING,        /* bInterfaceSubClass */
	AUDIO_PROTOCOL_UNDEFINED,             /* bInterfaceProtocol */
	0x00,                                 /* iInterface */
	/* 09 byte*/

	/* USB Speaker Standard AS Interface Descriptor - Audio Streaming Operational */
	/* Interface 1, Alternate Setting 1                                           */
	AUDIO_INTERFACE_DESC_SIZE,  /* bLength */
	USB_INTERFACE_DESCRIPTOR_TYPE,        /* bDescriptorType */
	0x01,                                 /* bInterfaceNumber */
	0x01,                                 /* bAlternateSetting */
	0x01,                                 /* bNumEndpoints */
	USB_DEVICE_CLASS_AUDIO,               /* bInterfaceClass */
	AUDIO_SUBCLASS_AUDIOSTREAMING,        /* bInterfaceSubClass */
	AUDIO_PROTOCOL_UNDEFINED,             /* bInterfaceProtocol */
	0x00,                                 /* iInterface */
	/* 09 byte*/

	/* USB Speaker Audio Streaming Interface Descriptor */
	AUDIO_STREAMING_INTERFACE_DESC_SIZE,  /* bLength */
	AUDIO_INTERFACE_DESCRIPTOR_TYPE,      /* bDescriptorType */
	AUDIO_STREAMING_GENERAL,              /* bDescriptorSubtype */
	0x01,                                 /* bTerminalLink */
	0x01,                                 /* bDelay */
	0x01,                                 /* wFormatTag AUDIO_FORMAT_PCM  0x0001*/
	0x00,
	/* 07 byte*/

	/* USB Speaker Audio Type III Format Interface Descriptor */
	0x0B,                                 /* bLength */
	AUDIO_INTERFACE_DESCRIPTOR_TYPE,      /* bDescriptorType */
	AUDIO_STREAMING_FORMAT_TYPE,          /* bDescriptorSubtype */
	AUDIO_FORMAT_TYPE_III,                /* bFormatType */
	0x02,                                 /* bNrChannels */
	0x02,                                 /* bSubFrameSize :  2 Bytes per frame (16bits) */
	16,                                   /* bBitResolution (16-bits per sample) */
	0x01,                                 /* bSamFreqType only one frequency supported */
	SAMPLE_FREQ(USBD_AUDIO_FREQ),         /* Audio sampling frequency coded on 3 bytes */
	/* 11 byte*/

	/* Endpoint 1 - Standard Descriptor */
	AUDIO_STANDARD_ENDPOINT_DESC_SIZE,    /* bLength */
	USB_ENDPOINT_DESCRIPTOR_TYPE,         /* bDescriptorType */
	AUDIO_OUT_EP,                         /* bEndpointAddress 1 out endpoint*/
	USB_ENDPOINT_TYPE_ISOCHRONOUS,        /* bmAttributes */
	AUDIO_PACKET_SZE(USBD_AUDIO_FREQ),    /* wMaxPacketSize in Bytes (Freq(Samples)*2(Stereo)*2(HalfWord)) */
	0x01,                                 /* bInterval */
	0x00,                                 /* bRefresh */
	0x00,                                 /* bSynchAddress */
	/* 09 byte*/

	/* Endpoint - Audio Streaming Descriptor*/
	AUDIO_STREAMING_ENDPOINT_DESC_SIZE,   /* bLength */
	AUDIO_ENDPOINT_DESCRIPTOR_TYPE,       /* bDescriptorType */
	AUDIO_ENDPOINT_GENERAL,               /* bDescriptor */
	0x00,                                 /* bmAttributes */
	0x00,                                 /* bLockDelayUnits */
	0x00,                                 /* wLockDelay */
	0x00,
	/* 07 byte*/
} ;

/**
    @}
*/

/** @defgroup usbd_audio_Private_Functions
    @{
*/

/**
    @brief  usbd_audio_Init
          Initilaizes the AUDIO interface.
    @param  pdev: device instance
    @param  cfgidx: Configuration index
    @retval status
*/
static uint8_t  usbd_audio_Init (void  *pdev,
                                 uint8_t cfgidx)
{
	/* Open EP OUT */
	DCD_EP_Open(pdev,
	            AUDIO_OUT_EP,
	            AUDIO_OUT_PACKET,
	            USB_OTG_EP_ISOC);
	/* Initialize the Audio output Hardware layer */
	if (AUDIO_OUT_fops.Init(USBD_AUDIO_FREQ, DEFAULT_VOLUME, 0) != USBD_OK)
		return USBD_FAIL;
	/* Prepare Out endpoint to receive audio data */
	DCD_EP_PrepareRx(pdev,
	                 AUDIO_OUT_EP,
	                 (uint8_t*)IsocOutBuff,
	                 AUDIO_OUT_PACKET);
	return USBD_OK;
}

/**
    @brief  usbd_audio_Init
          DeInitializes the AUDIO layer.
    @param  pdev: device instance
    @param  cfgidx: Configuration index
    @retval status
*/
static uint8_t  usbd_audio_DeInit (void  *pdev,
                                   uint8_t cfgidx)
{
	DCD_EP_Close (pdev, AUDIO_OUT_EP);
	/* DeInitialize the Audio output Hardware layer */
	if (AUDIO_OUT_fops.DeInit(0) != USBD_OK)
		return USBD_FAIL;
	return USBD_OK;
}

/**
    @brief  usbd_audio_Setup
            Handles the Audio control request parsing.
    @param  pdev: instance
    @param  req: usb requests
    @retval status
*/
static uint8_t  usbd_audio_Setup (void  *pdev,
                                  USB_SETUP_REQ *req)
{
	uint16_t len=USB_AUDIO_DESC_SIZ;
	uint8_t  *pbuf=usbd_audio_CfgDesc + 18;
	switch (req->bmRequest & USB_REQ_TYPE_MASK)
	{
		/* AUDIO Class Requests -------------------------------*/
		case USB_REQ_TYPE_CLASS :
			switch (req->bRequest)
			{
				case AUDIO_REQ_GET_CUR:
					AUDIO_Req_GetCurrent(pdev, req);
					break;
				case AUDIO_REQ_SET_CUR:
					AUDIO_Req_SetCurrent(pdev, req);
					break;
				default:
					USBD_CtlError (pdev, req);
					return USBD_FAIL;
			}
			break;
		/* Standard Requests -------------------------------*/
		case USB_REQ_TYPE_STANDARD:
			switch (req->bRequest)
			{
				case USB_REQ_GET_DESCRIPTOR:
					if( (req->wValue >> 8) == AUDIO_DESCRIPTOR_TYPE)
					{
#ifdef USB_OTG_HS_INTERNAL_DMA_ENABLED
						pbuf = usbd_audio_Desc;
#else
						pbuf = usbd_audio_CfgDesc + 18;
#endif
						len = MIN(USB_AUDIO_DESC_SIZ, req->wLength);
					}
					USBD_CtlSendData (pdev,
					                  pbuf,
					                  len);
					break;
				case USB_REQ_GET_INTERFACE :
					USBD_CtlSendData (pdev,
					                  (uint8_t *)&usbd_audio_AltSet,
					                  1);
					break;
				case USB_REQ_SET_INTERFACE :
					if ((uint8_t)(req->wValue) < AUDIO_TOTAL_IF_NUM)
						usbd_audio_AltSet = (uint8_t)(req->wValue);
					else
					{
						/* Call the error management function (command will be nacked */
						USBD_CtlError (pdev, req);
					}
					break;
			}
	}
	return USBD_OK;
}

/**
    @brief  usbd_audio_EP0_RxReady
            Handles audio control requests data.
    @param  pdev: device device instance
    @retval status
*/
static uint8_t  usbd_audio_EP0_RxReady (void  *pdev)
{
	/* Check if an AudioControl request has been issued */
	if (AudioCtlCmd == AUDIO_REQ_SET_CUR)
	{
		/* In this driver, to simplify code, only SET_CUR request is managed */
		/* Check for which addressed unit the AudioControl request has been issued */
		if (AudioCtlUnit == AUDIO_OUT_STREAMING_CTRL)
		{
			/* In this driver, to simplify code, only one unit is manage */
			/* Call the audio interface mute function */
			AUDIO_OUT_fops.MuteCtl(AudioCtl[0]);
			/* Reset the AudioCtlCmd variable to prevent re-entering this function */
			AudioCtlCmd = 0;
			AudioCtlLen = 0;
		}
	}
	return USBD_OK;
}

/**
    @brief  usbd_audio_DataIn
            Handles the audio IN data stage.
    @param  pdev: instance
    @param  epnum: endpoint number
    @retval status
*/
static uint8_t  usbd_audio_DataIn (void *pdev, uint8_t epnum)
{
	return USBD_OK;
}

/**
    @brief  usbd_audio_DataOut
            Handles the Audio Out data stage.
    @param  pdev: instance
    @param  epnum: endpoint number
    @retval status
*/
static uint8_t  usbd_audio_DataOut (void *pdev, uint8_t epnum)
{
	if (epnum == AUDIO_OUT_EP)
	{
		/* Increment the Buffer pointer or roll it back when all buffers are full */
		if (IsocOutWrPtr >= (IsocOutBuff + (AUDIO_OUT_PACKET * OUT_PACKET_NUM)))
		{
			/* All buffers are full: roll back */
			IsocOutWrPtr = IsocOutBuff;
		}
		else
		{
			/* Increment the buffer pointer */
			IsocOutWrPtr += AUDIO_OUT_PACKET;
		}
		/* Toggle the frame index */
		((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].even_odd_frame =
		    (((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].even_odd_frame)? 0:1;
		/* Prepare Out endpoint to receive next audio packet */
		DCD_EP_PrepareRx(pdev,
		                 AUDIO_OUT_EP,
		                 (uint8_t*)(IsocOutWrPtr),
		                 AUDIO_OUT_PACKET);
		/* Trigger the start of streaming only when half buffer is full */
		if ((PlayFlag == 0) && (IsocOutWrPtr >= (IsocOutBuff + ((AUDIO_OUT_PACKET * OUT_PACKET_NUM) / 2))))
		{
			/* Enable start of Streaming */
			PlayFlag = 1;
		}
	}
	return USBD_OK;
}

/**
    @brief  usbd_audio_SOF
            Handles the SOF event (data buffer update and synchronization).
    @param  pdev: instance
    @param  epnum: endpoint number
    @retval status
*/
static uint8_t  usbd_audio_SOF (void *pdev)
{
	/*  Check if there are available data in stream buffer.
	    In this function, a single variable (PlayFlag) is used to avoid software delays.
	    The play operation must be executed as soon as possible after the SOF detection. */
	if (PlayFlag)
	{
		/* Start playing received packet */
		AUDIO_OUT_fops.AudioCmd((uint8_t*)(IsocOutRdPtr),  /* Samples buffer pointer */
		                        AUDIO_OUT_PACKET,          /* Number of samples in Bytes */
		                        AUDIO_CMD_PLAY);           /* Command to be processed */
		/* Increment the Buffer pointer or roll it back when all buffers all full */
		if (IsocOutRdPtr >= (IsocOutBuff + (AUDIO_OUT_PACKET * OUT_PACKET_NUM)))
		{
			/* Roll back to the start of buffer */
			IsocOutRdPtr = IsocOutBuff;
		}
		else
		{
			/* Increment to the next sub-buffer */
			IsocOutRdPtr += AUDIO_OUT_PACKET;
		}
		/* If all available buffers have been consumed, stop playing */
		if (IsocOutRdPtr == IsocOutWrPtr)
		{
			/* Pause the audio stream */
			AUDIO_OUT_fops.AudioCmd((uint8_t*)(IsocOutBuff),   /* Samples buffer pointer */
			                        AUDIO_OUT_PACKET,          /* Number of samples in Bytes */
			                        AUDIO_CMD_PAUSE);          /* Command to be processed */
			/* Stop entering play loop */
			PlayFlag = 0;
			/* Reset buffer pointers */
			IsocOutRdPtr = IsocOutBuff;
			IsocOutWrPtr = IsocOutBuff;
		}
	}
	return USBD_OK;
}

/**
    @brief  usbd_audio_OUT_Incplt
            Handles the iso out incomplete event.
    @param  pdev: instance
    @retval status
*/
static uint8_t  usbd_audio_OUT_Incplt (void  *pdev)
{
	return USBD_OK;
}

/******************************************************************************
     AUDIO Class requests management
******************************************************************************/
/**
    @brief  AUDIO_Req_GetCurrent
            Handles the GET_CUR Audio control request.
    @param  pdev: instance
    @param  req: setup class request
    @retval status
*/
static void AUDIO_Req_GetCurrent(void *pdev, USB_SETUP_REQ *req)
{
	/* Send the current mute state */
	USBD_CtlSendData (pdev,
	                  AudioCtl,
	                  req->wLength);
}

/**
    @brief  AUDIO_Req_SetCurrent
            Handles the SET_CUR Audio control request.
    @param  pdev: instance
    @param  req: setup class request
    @retval status
*/
static void AUDIO_Req_SetCurrent(void *pdev, USB_SETUP_REQ *req)
{
	if (req->wLength)
	{
		/* Prepare the reception of the buffer over EP0 */
		USBD_CtlPrepareRx (pdev,
		                   AudioCtl,
		                   req->wLength);
		/*  Set the global variables indicating current request and its length
		    to the function usbd_audio_EP0_RxReady() which will process the request */
		AudioCtlCmd = AUDIO_REQ_SET_CUR;     /* Set the request value */
		AudioCtlLen = req->wLength;          /* Set the request data length */
		AudioCtlUnit = HIBYTE(req->wIndex);  /* Set the request target unit */
	}
}

/**
    @brief  USBD_audio_GetCfgDesc
            Returns configuration descriptor.
    @param  speed : current device speed
    @param  length : pointer data length
    @retval pointer to descriptor buffer
*/
static uint8_t  *USBD_audio_GetCfgDesc (uint8_t speed, uint16_t *length)
{
	*length = sizeof (usbd_audio_CfgDesc);
	return usbd_audio_CfgDesc;
}
/**
    @}
*/

/**
    @}
*/

/**
    @}
*/

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
